package core;

import java.io.IOException;
import java.io.FileWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import response.*;

/**
 * A game server used for nursing MMO-RPG. Especially designed for protocol
 * between Panda3D (python) and Java socket communication.
 *
 * @author yiyi, Supakit, Devang, Zoran, Kaven, Jason, Gary
 *
 */
public class GameServer {

     private HashMap<String, GameMap> mapList;
    //man- private HashMap<Integer, GameParty> partyList;
    //man- private HashMap<Integer, GameBattle> battleList;
    //man- private HashMap<Integer, GameInvitation> invitationList;
    //man- private HashMap<Integer, GameTrade> tradeList;
    private HashMap<Integer, GameMode> modeList;
    //man- private HashMap<Integer, GameDuel> duelList;
    // List of Currently Active Users
    private HashMap<Integer, GameUser> activeUserMap;
    // List of Currently Active Characters -> Map
    private HashMap<Integer, String> activeCharacterMap;
    private HashMap<String, Vector<Message>> privateChats;
    private HashMap<String, Vector<Message>> buddyInvites;
    // List of Currently Active Bugs -> Map
    //private HashMap<Integer, String> activeBugMap;
    // List of Currently Active NPCs -> Map
    //private HashMap<Integer, String> activeNPCMap;
    // A list of all active Game Clients / Threads
    private HashMap<Long, GameClient> activeThreads;
    // List of Pending Bugs
    //man- private HashMap<Integer, GameBug> inActiveBugMap;
    // Game Database
    private GameDB db;
    // Game Save Timer
    private GameTimer saveTimer;
    // Game Configuration
    private GameServerConfig gameConfigReader;
    //man- private BugServerMonitor bugServerMonitor;
    //man- private GameEntityLoader entityLoader;
    //man- private GameBoardParser gameBoardParser;
    //man- private GameMapParser gameMapParser;
    //man- private GameExperienceParser gameExpParser;
    //man- private GameSurvivalModeParser gameSurvivalModeParser;
    // Game Script Handler
    //man- private GameScriptHandler scriptHandler;
    // Debug Mode Flag
    public static boolean debugMode;
    public static boolean smartBugMode;
    // Initialize Object (i.e. Bug, NPC, Warp) Tracking ID
    private int uniqueID = Constants.RANDOM_ID;



    public GameServer() {

        gameConfigReader = new GameServerConfig();
        gameConfigReader.configure();

        try {
            db = new GameDB(this);
            db.resetOnlineStatus();
        } catch (SQLException e) {
            log("Could not connect to db:","e");
            e.printStackTrace(System.err);
            System.exit(-5);
        }

        /*man-
        mapList = new HashMap<String, GameMap>();
        partyList = new HashMap<Integer, GameParty>();
        battleList = new HashMap<Integer, GameBattle>();
        invitationList = new HashMap<Integer, GameInvitation>();
        tradeList = new HashMap<Integer, GameTrade>();*/
        modeList = new HashMap<Integer, GameMode>();
        /*man-
        duelList = new HashMap<Integer, GameDuel>();
        */

        buddyInvites = new HashMap<String, Vector<Message>>();
        privateChats = new HashMap<String, Vector<Message>>();
        
        // Initialize User Container
        activeUserMap = new HashMap<Integer, GameUser>();
        // Initialize Character Container
        activeCharacterMap = new HashMap<Integer, String>();
        /*man-
        // Initialize Bug Container
        //activeBugMap = new HashMap<Integer, String>();
        // Initialize NPC Container
        //activeNPCMap = new HashMap<Integer, String>();*/
        // Initialize list of active Threads
        activeThreads = new HashMap<Long, GameClient>();

        /*man-
        inActiveBugMap = new HashMap<Integer, GameBug>();

        gameMapParser = new GameMapParser(this);
        gameMapParser.configure();

        gameExpParser = new GameExperienceParser();
        gameExpParser.configure();

        gameSurvivalModeParser = new GameSurvivalModeParser(this);
        gameSurvivalModeParser.configure();*/

        GameRequestTable.init();

        /*man-
        GameItemEffectTable.gameServer = this;
        GameItemEffectCollection.gameServer = this;
        GameTimerCollection.gameServer = this;

        saveTimer = new GameTimer();
        saveTimer.schedule(new GameTimerCollection.SaveTimer(), Constants.AUTO_SAVE_DELAY, Constants.AUTO_SAVE_DELAY);

        scriptHandler = new GameScriptHandler(this);
        scriptHandler.init();

        entityLoader = new GameEntityLoader(this);
        entityLoader.configure();

        gameBoardParser = new GameBoardParser(this);
        gameBoardParser.configure();*/
    }

    /**
     * Start the server
     */
    public void runServer() {
        // Socket where server will be listening
        ServerSocket listenSocket;
        int serverPort = gameConfigReader.getServerPort();
        //int serverPort = 8080;

        try {
            listenSocket = new ServerSocket(serverPort);

            System.out.println("Server has started: " + listenSocket.getLocalPort());

            /*man-
            if (gameConfigReader.getBugServerPath() != null) {
                System.out.println("Loading Bug Server Monitor...");

                bugServerMonitor = new BugServerMonitor(this, gameConfigReader.getBugServerPath());
                bugServerMonitor.start();
            } else {
                System.out.println("Ignoring Bug Server Path...");
            }*/

            while (true) {
                System.out.println("Waiting for client...");

                try {
                    Socket clientSocket = listenSocket.accept();
                    System.out.println("Connection Established!");

                    GameClient client = new GameClient(clientSocket, this);
                    activeThreads.put(client.getId(), client);
                    client.start();
                } catch (IOException e) {
                    log(e.getMessage());
                }
            }
        } catch (IOException e) {
            System.out.println("Exception in port: " + serverPort + " listening: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }

    /**
     * Shutdown Game Server
     */
    public void stopServer() {
        // Release resources, kill the Game Server
        // Stop all Threads
        for (GameClient client : activeThreads.values()) {
            client.end();
        }
        // Exit.
        System.exit(0);
    }

    /**
     * Logs in a user.
     *
     * @param username	username passed in
     * @param password	password passed in
     * @return GameUser if success, else NULL.
     */
    public final short login(String username, String password, GameUser user) {
        log(username + " is connecting...");

        int user_id = db.validateUser(username, password);
        short status = 0;
        if (user_id != -1) {
            GameUser activeUser = getActiveUser(user_id);

            if (activeUser != null) {
                status = 2;
                ResponseLogout response = new ResponseLogout();
                response.set(this);
                response.setOut(getClientThread(activeUser.getClientID()).getOutput());
                response.setStatus(status);
                response.run();

                getClientThread(activeUser.getClientID()).end();
                log(username + " logged out");
            } else {
                db.setUserOnline(user_id, 1);
                db.setLastLoginTime(user_id);
                db.getUser(user_id, user);
                user.setOnline(true);
                addActiveUser(user);
                /**man-
                if (user_id == Constants.BUG_USER_ID) {
                    if (smartBugMode) {
                        if (bugServerMonitor != null) {
                            if (bugServerMonitor.isAlive()) {
                                if (bugServerMonitor.getState() == Thread.State.TIMED_WAITING) {
                                    if (BugServerMonitor.DELAY_MODE == BugServerMonitor.START_DELAY) {
                                        bugServerMonitor.override();
                                    }

                                    bugServerMonitor.interrupt();
                                } else if (bugServerMonitor.getState() == Thread.State.WAITING) {
                                    bugServerMonitor.override();
                                    bugServerMonitor.interrupt();
                                }
                            }
                        }
                    }
                }*/
            }
        } else {
            status = 1;
            log(username + " has failed to log in.");
        }
        return status;
    }


    public GameCharacter selectCharacter(int user_id, int char_num) {
        GameCharacter character = db.getCharacterBySlot(user_id, char_num);
        //manori added
        character.setOnline(true);
        /*man-
        if (character != null) {
            if (character.getMapID() >= Constants.RANDOM_ID) {
                character.setMapID(character.getSaveMapID());
                character.setPos(character.getSavePos());
            }

            character.setQuestionRatio(db.getQuestionRatio(character.getID()));

            character.setOnline(true);
            addActiveCharacter(character);

            if (smartBugMode) {
                if (bugServerMonitor != null) {
                    if (bugServerMonitor.isAlive()) {
                        if (bugServerMonitor.getState() == Thread.State.TIMED_WAITING) {
                            if (BugServerMonitor.DELAY_MODE == BugServerMonitor.STOP_DELAY) {
                                bugServerMonitor.interrupt();
                            }
                        } else if (bugServerMonitor.getState() == Thread.State.WAITING) {
                            bugServerMonitor.interrupt();
                        }
                    }
                }
            }
        }*/

        return character;
    }

    public GameDB getGameDB() {
        return db;
    }
    /*man-
    public GameEntityLoader getGameEntityLoader() {
        return entityLoader;
    }*/

    /*man-
    public GameScriptHandler getGameScriptHandler() {
        return scriptHandler;
    }*/

    public GameServerConfig getGameConfigReader() {
        return gameConfigReader;
    }

    /*man-
    public GameExperienceParser getGameExperienceParser() {
        return gameExpParser;
    }

    public GameMap getMap(int map_id) {
        return mapList.get(map_id);
    }

    public GameMap setMap(int map_id, GameMap map) {
        return mapList.put(map_id, map);
    }

    public GameMap removeMap(int map_id) {
        GameMap map = mapList.remove(map_id);

        if (map != null) {
            for (GameBug bug : getActiveBugList(map_id)) {
                removeActiveBug(bug.getID());
            }

            ResponseRemoveMap response = new ResponseRemoveMap();
            response.set(this);
            response.setMapID(map.getID());

            updateUserQueue(response, getActiveUser(Constants.BUG_USER_ID));
        }

        return map;
    }

    public Vector<GameMap> getMapList() {
        return new Vector<GameMap>(mapList.values());
    }

    public GameMap createMapInstance(int map_id) {
        GameMap map = gameMapParser.getMap(map_id);

        if (map != null) {
            map_id = Integer.valueOf(getUniqueID() + String.valueOf(map_id));

            map = new GameMap(map_id, "", map);
            mapList.put(map_id, map);

            ResponseCreateMap response = new ResponseCreateMap();
            response.set(this);
            response.setMap(map);

            updateUserQueue(response, getActiveUser(Constants.BUG_USER_ID));
        }

        return map;
    }*/

    /*man-
    public GameInvitation getInvitation(int invitation_id) {
        return invitationList.get(invitation_id);
    }

    public GameInvitation createInvitation(short type, int source_id, int target_id) {
        GameInvitation invitation = new GameInvitation(getUniqueID(), type);
        invitation.setSourceID(source_id);
        invitation.setTargetID(target_id);

        getActiveCharacter(target_id).setInvitation(invitation);
        invitationList.put(invitation.getID(), invitation);

        return invitation;
    }

    public GameInvitation removeInvitation(int invitation_id) {
        GameInvitation invitation = invitationList.remove(invitation_id);

        if (invitation != null) {
            getActiveCharacter(invitation.getTargetID()).removeInvitation(invitation_id);
        }

        return invitation;
    }

    public GameTrade getTrade(int trade_id) {
        return tradeList.get(trade_id);
    }

    public GameTrade createTrade(int trade_id, int trader_id, int other_id) {
        return tradeList.put(trade_id, new GameTrade(trade_id, trader_id, other_id));
    }

    public GameTrade removeTrade(int trade_id) {
        GameTrade trade = tradeList.remove(trade_id);

        if (trade != null) {
            GameCharacter character = getActiveCharacter(trade.getTraderID());

            if (character != null) {
                character.setTradeID(0);
            }

            character = getActiveCharacter(trade.getOtherID());

            if (character != null) {
                character.setTradeID(0);
            }
        }

        return trade;
    }

    public int getLastUniqueID() {
        return uniqueID;
    }

    public int getUniqueID() {
        return uniqueID++;
    }*/



    /**
     * Logs out a user. Essentially, does three things:
     * - keeps the last known user's sceneId
     * (copy from DB->user->scene_id to DB->user->logout_scene_id)
     * - sets "last logout time"
     * - removes sceneId by replacing it with NULL
     *
     * @param username
     * @return true is success, else false
     */
    public final void logout(GameUser user) {
        db.setPlayTime(user.getID(), (int) ((System.currentTimeMillis() - user.getLastSaveTime()) / 1000));

        if (user.getID() != Constants.BUG_USER_ID) {

            GameCharacter character = user.getCharacter();

            if (character != null) {
                //character.stopHealthRegenTimer();

                db.saveGameUser(character);
                db.setCharacterOnline(character.getID(), 0);
                character.setOnline(false);
                /*man-
                if (character.getMapID() > Constants.RANDOM_ID) {
                    db.setLastMapID(character.getID(), character.getSaveMapID());
                    db.setLastPosition(character.getID(), character.getSavePos());
                }

                removeActiveCharacter(character.getID());

                ResponseRemoveUser response = new ResponseRemoveUser();
                response.set(this);
                response.setTargetID(character.getID());

                updateOthersQueue(response, getActiveUserList(character.getMapID()), user.getID());*/

                /*man-
                for (GameCharacter buddy : db.getBuddyOnlineList(character.getID(), true)) {
                    ResponseBuddyOnline buddyResponse = new ResponseBuddyOnline();
                    buddyResponse.set(this);
                    buddyResponse.setCharacter(character);

                    updateUserQueue(buddyResponse, getActiveUser(buddy.getUserID()));
                }

                if (character.getPartyID() != 0) {
                    GameParty party = getParty(character.getPartyID());

                    party.removeMember(character.getID());

                    if (party.getPartyList().isEmpty()) {
                        removeParty(character.getPartyID());
                    } else {
                        ResponsePartyOnline partyResponse = new ResponsePartyOnline();
                        partyResponse.set(this);
                        partyResponse.setCharacter(character);

                        updateOthersQueue(partyResponse, getActivePartyUserList(character.getPartyID()), user.getID());
                    }
                }*/

                if (character.getGameMode() != 0) {
                    GameMode mode = modeList.get(character.getGameMode());

                    if (mode != null) {
                        mode.removePlayer(character.getID());
                    }
                }
                /*
                if (character.getTradeID() != 0) {
                    removeTrade(character.getTradeID());
                }*/

                System.out.println(character.getName() + " has left the game.");
            }
        } else {
            System.out.println("Exterminating...");
            /**man-
            for (GameBug bug : getActiveBugList()) {
                ResponseRemoveUser removeResponse = new ResponseRemoveUser();
                removeResponse.set(this);
                removeResponse.setTargetID(bug.getID());

                updateOthersQueue(removeResponse, getActiveUserList(bug.getMapID()), bug.getID());

                removeActiveBug(bug.getID());
            }*/
        }
        removeActiveUser(user.getID());
        db.setUserOnline(user.getID(), 0);
        db.setLastLogoutTime(user.getID());
    }

    public GameClient addClientThread(GameClient client) {
        return activeThreads.put(client.getId(), client);
    }

    public GameClient getClientThread(long thread_id) {
        return activeThreads.get(thread_id);
    }

    public GameClient removeClientThread(long thread_id) {
        return activeThreads.remove(thread_id);
    }


    public GameUser addActiveUser(GameUser user) {
        return activeUserMap.put(user.getID(), user);
    }

    public GameUser getActiveUser(int user_id) {
        return activeUserMap.get(user_id);
    }

    public GameUser removeActiveUser(int user_id) {
        return activeUserMap.remove(user_id);
    }

    /*man-
    public GameEntity getActiveEntity(int target_id) {
        GameEntity target = null;

        if (activeCharacterMap.containsKey(target_id)) {
            target = getActiveCharacter(target_id);
        } else if (activeBugMap.containsKey(target_id)) {
            target = getActiveBug(target_id);
        } else if (activeNPCMap.containsKey(target_id)) {
            target = getActiveNPC(target_id);
        }

        return target;
    }*/

    /*man-
     public void addActiveCharacter(GameCharacter character) {
        mapList.get(character.getMapID()).setCharacter(character);
        activeCharacterMap.put(character.getID(), character.getMapID());
    }

    public GameCharacter getActiveCharacter(int char_id) {
        GameCharacter character = null;

        if (activeCharacterMap.containsKey(char_id)) {
            GameMap map = mapList.get(activeCharacterMap.get(char_id));

            if (map != null) {
                character = map.getCharacter(char_id);
            }
        }

        return character;
    }

    public GameCharacter getActiveCharacter(String name) {
        return getActiveCharacter(db.getCharacterID(name));
    }

    public GameCharacter removeActiveCharacter(int char_id) {
        GameCharacter character = null;

        if (activeCharacterMap.containsKey(char_id)) {
            GameMap map = mapList.get(activeCharacterMap.get(char_id));

            if (map != null) {
                character = map.removeCharacter(char_id);
                activeCharacterMap.remove(char_id);

                Vector<GameBattle> bList = character.getBattleList();

                for (int i = bList.size() - 1; i >= 0; i--) {
                    GameBattle battle = bList.get(i);

                    if (battle != null) {
                        battle.removeCredit(char_id);

                        GameBug bug = battle.getTarget();

                        if (bug != null) {
                            bug.removeTarget(char_id);

                            if (bug.getTargetList().isEmpty()) {
                                removeBattle(battle.getID());
                            }
                        }
                    }
                }

                character.clearBattleList();
                character.clearTargetList();
                character.clearInvitationList();
            }
        }

        return character;
    }*/

    /*man-
    public void addActiveBug(GameBug bug) {
        mapList.get(bug.getMapID()).setBug(bug);
        activeBugMap.put(bug.getID(), bug.getMapID());
    }

    public GameBug getActiveBug(int bug_id) {
        GameBug bug = null;

        if (activeBugMap.containsKey(bug_id)) {
            GameMap map = mapList.get(activeBugMap.get(bug_id));

            if (map != null) {
                bug = map.getBug(bug_id);
            }
        }

        return bug;
    }

    public GameBug removeActiveBug(int bug_id) {
        GameBug bug = null;

        if (activeBugMap.containsKey(bug_id)) {
            GameMap map = mapList.get(activeBugMap.get(bug_id));

            if (map != null) {
                bug = map.removeBug(bug_id);
                activeBugMap.remove(bug_id);

                for (GameCharacter target : getActiveCharacterList(bug.getTargetList())) {
                    target.removeTarget(bug_id);
                }

                removeBattle(bug_id);
            }
        }

        return bug;
    }*/

    /*man-
     public void addActiveNPC(GameNPC npc) {
        mapList.get(npc.getMapID()).setNPC(npc);
        activeNPCMap.put(npc.getID(), npc.getMapID());
    }

    public GameNPC getActiveNPC(int npc_id) {
        GameNPC npc = null;

        if (activeNPCMap.containsKey(npc_id)) {
            GameMap map = mapList.get(activeNPCMap.get(npc_id));

            if (map != null) {
                npc = map.getNPC(npc_id);
            }
        }

        return npc;
    }

    public GameNPC removeActiveNPC(int npc_id) {
        GameNPC npc = null;

        if (activeNPCMap.containsKey(npc_id)) {
            GameMap map = mapList.get(activeNPCMap.get(npc_id));

            if (map != null) {
                npc = map.removeNPC(npc_id);
                activeNPCMap.remove(npc_id);
            }
        }

        return npc;
    }*/

    /*man-
    public GameBug addInActiveBug(GameBug bug) {
        return inActiveBugMap.put(bug.getID(), bug);
    }

    public GameBug getInActiveBug(int bug_id) {
        return inActiveBugMap.get(bug_id);
    }

    public GameBug removeInActiveBug(int bug_id) {
        return inActiveBugMap.remove(bug_id);
    }*/

    /*man-
    public Vector<GameUser> getActiveUserList() {
        return new Vector<GameUser>(activeUserMap.values());
    }

    public Vector<GameUser> getActiveUserList(int map_id) {
        Vector<GameUser> userList = new Vector<GameUser>();
        GameMap map = mapList.get(map_id);

        if (map != null) {
            userList = map.getUserList();
        }

        return userList;
    }*/

    public Vector<GameUser> getActiveUserList(Vector<Integer> idList) {
        Vector<GameUser> userList = new Vector<GameUser>();

        for (int user_id : idList) {
            GameUser user = getActiveUser(user_id);

            if (user != null) {
                userList.add(user);
            }
        }

        return userList;
    }

    /*man-
    public Vector<GameCharacter> getActiveCharacterList() {
        Vector<GameCharacter> characterList = new Vector<GameCharacter>();

        for (GameMap map : mapList.values()) {
            characterList.addAll(map.getCharacterList());
        }

        return characterList;
    }

    public Vector<GameCharacter> getActiveCharacterList(int map_id) {
        Vector<GameCharacter> characterList = new Vector<GameCharacter>();
        GameMap map = mapList.get(map_id);

        if (map != null) {
            characterList = map.getCharacterList();
        }

        return characterList;
    }

    public Vector<GameCharacter> getActiveCharacterList(List<Integer> idList) {
        Vector<GameCharacter> characterList = new Vector<GameCharacter>();

        for (int char_id : idList) {
            GameCharacter character = getActiveCharacter(char_id);

            if (character != null) {
                characterList.add(character);
            }
        }

        return characterList;
    }*/

    /*man-
    public Vector<GameBug> getActiveBugList() {
        Vector<GameBug> bugList = new Vector<GameBug>();

        for (GameMap map : mapList.values()) {
            bugList.addAll(map.getBugList());
        }

        return bugList;
    }

    public Vector<GameBug> getActiveBugList(int map_id) {
        Vector<GameBug> bugList = new Vector<GameBug>();
        GameMap map = mapList.get(map_id);

        if (map != null) {
            bugList = map.getBugList();
        }

        return bugList;
    }

    public Vector<GameBug> getActiveBugList(Vector<Integer> idList) {
        Vector<GameBug> bugList = new Vector<GameBug>();

        for (int bug_id : idList) {
            GameBug bug = getActiveBug(bug_id);

            if (bug != null) {
                bugList.add(bug);
            }
        }

        return bugList;
    }*/

    /*man-
    public Vector<GameNPC> getActiveNPCList() {
        Vector<GameNPC> npcList = new Vector<GameNPC>();

        for (GameMap map : mapList.values()) {
            npcList.addAll(map.getNPCList());
        }

        return npcList;
    }

    public Vector<GameNPC> getActiveNPCList(int map_id) {
        Vector<GameNPC> npcList = new Vector<GameNPC>();
        GameMap map = mapList.get(map_id);

        if (map != null) {
            npcList = map.getNPCList();
        }

        return npcList;
    }

    public Vector<GameNPC> getActiveNPCList(Vector<Integer> idList) {
        Vector<GameNPC> npcList = new Vector<GameNPC>();

        for (int npc_id : idList) {
            GameNPC npc = getActiveNPC(npc_id);

            if (npc != null) {
                npcList.add(npc);
            }
        }

        return npcList;
    }*/

    /*man-
    public Vector<GameUser> getActivePartyUserList(int party_id) {
        Vector<GameUser> userList = new Vector<GameUser>();
        GameParty party = partyList.get(party_id);

        if (party != null) {
            for (GameCharacter character : party.getPartyList()) {
                userList.add(getActiveUser(character.getUserID()));
            }
        }

        return userList;
    }

    public Vector<GameUser> getActivePartyUserList(int party_id, int map_id) {
        Vector<GameUser> userList = new Vector<GameUser>();
        GameParty party = partyList.get(party_id);

        if (party != null) {
            for (GameCharacter character : party.getPartyList()) {
                if (character.getMapID() == map_id) {
                    userList.add(getActiveUser(character.getUserID()));
                }
            }
        }

        return userList;
    }*/

    /*man-
    public Vector<GameUser> getActiveBattleUserList(int battle_id) {
        Vector<GameUser> userList = new Vector<GameUser>();
        GameBattle battle = battleList.get(battle_id);

        if (battle != null) {
            for (GameCharacter character : battle.getAttackerList()) {
                userList.add(getActiveUser(character.getUserID()));
            }

            userList.add(getActiveUser(battle.getTarget().getUserID()));
        }

        return userList;
    }*/

    public Vector<GameUser> getActiveModeUserList(int mode_id) {
        Vector<GameUser> userList = new Vector<GameUser>();
        GameMode mode = modeList.get(mode_id);

        if (mode != null) {
            for (GameCharacter character : mode.getPlayerList()) {
                userList.add(getActiveUser(character.getUserID()));
            }
        }

        return userList;
    }

    /*man-
    public Vector<GameUser> getActiveDuelUserList(int duel_id) {
        Vector<GameUser> userList = new Vector<GameUser>();
        GameDuel duel = duelList.get(duel_id);

        if (duel != null) {
            userList.add(getActiveUser(duel.getAttacker().getUserID()));
            userList.add(getActiveUser(duel.getTarget().getUserID()));
        }

        return userList;
    }

    public GameParty getParty(int party_id) {
        return partyList.get(party_id);
    }

    public GameParty setParty(int party_id, GameCharacter character) {
        GameParty party = partyList.get(party_id);

        if (party != null) {
            party.setMember(character);
        } else {
            int leader_id = db.getPartyLeader(party_id);

            if (leader_id != -1) {
                String name = db.getPartyName(party_id);

                party = new GameParty(party_id, leader_id, name);
                party.setMember(character);

                partyList.put(party_id, party);
            }
        }

        return party;
    }

    public GameParty removeParty(int party_id) {
        return partyList.remove(party_id);
    }

    public List<GameCharacter> getPartyList(int party_id) {
        List<GameCharacter> memberList = db.getParty(party_id);

        for (GameCharacter member : memberList) {
            GameCharacter character = getActiveCharacter(member.getID());

            if (character != null) {
                member.setOnline(character.getOnline());
                member.setHealthMax(character.getHealthMax());
                member.setHealth(character.getHealth());
            }
        }

        return memberList;
    }

    public GameBattle getBattle(int battle_id) {
        return battleList.get(battle_id);
    }

    public GameBattle setBattle(GameBattle battle) {
        return battleList.put(battle.getID(), battle);
    }

    public GameBattle removeBattle(int battle_id) {
        GameBattle battle = battleList.remove(battle_id);

        if (battle != null) {
            battle.end();
        }

        return battle;
    }

    public GameDuel getDuel(int duel_id) {
        return duelList.get(duel_id);
    }

    public GameDuel setDuel(GameDuel duel) {
        return duelList.put(duel.getID(), duel);
    }

    public GameDuel removeDuel(int duel_id) {
        GameDuel duel = duelList.remove(duel_id);

        if (duel != null) {
            duel.end();
        }

        return duel;
    }*/

    public GameMode getGameMode(int mode_id) {
        return modeList.get(mode_id);
    }

    public GameMode setGameMode(int mode_id, GameMode mode) {
        return modeList.put(mode_id, mode);
    }

    public GameMode removeGameMode(int mode_id) {
        GameMode mode = modeList.remove(mode_id);

        if (mode != null) {
            mode.end();

            //for (GameCharacter character : mode.getPlayerList()) {
                //warp(character.getID(), character.getSaveMapID(), character.getSavePos(), 0);
            //}

            //removeMap(mode.getMapID());
        }

        return mode;
    }

    /*man-
    public double getDistance(float[] a, float[] b) {
        return Math.sqrt(Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2) + Math.pow(b[2] - a[2], 2));
    }

    public void warp(int char_id, int map_id, float[] position, float heading) {
        warp(char_id, map_id, position[0], position[1], position[2], heading);
    }

    public void warp(int char_id, int map_id, float x, float y, float z, float h) {
        GameCharacter character = getActiveCharacter(char_id);

        if (character != null) {
            int lastMapID = character.getMapID();

            GameMap map = mapList.get(map_id);

            if (map != null) {
                if (map.getID() != lastMapID) {
                    db.setLastMapID(char_id, map.getID());

                    removeActiveCharacter(char_id);
                    character.setMapID(map.getID());
                    addActiveCharacter(character);
                }

                character.setPos(x, y, z);
                character.setH(0);

                ResponseMapChange response = new ResponseMapChange();
                response.set(this);
                response.setMap(map);
                response.setStatus(true);
                response.setWarpHeading(character.getH());
                response.setWarpPos(character.getPos());

                updateUserQueue(response, getActiveUser(character.getUserID()));

                ResponseRemoveUser removeResponse = new ResponseRemoveUser();
                removeResponse.set(this);
                removeResponse.setTargetID(character.getID());

                updateOthersQueue(removeResponse, getActiveUserList(lastMapID), character.getUserID());

                ResponseCreate createResponse = new ResponseCreate();
                createResponse.set(this);
                createResponse.setCharacter(character);
                createResponse.setUser(getActiveUser(character.getUserID()));

                updateOthersQueue(createResponse, getActiveUserList(map.getID()), character.getUserID());
            }
        }
    }

    public int startBoardGame(int char_id, short type, short mode, String topic, short min_level, short max_level) {
        int status = 0;

        GameBoard board = gameBoardParser.getGameBoard(type);

        if (board != null) {
            GameCharacter character = getActiveCharacter(char_id);

            if (character != null) {
                Vector<GameCharacter> playerList = partyList.get(character.getPartyID()).getPartyList(character.getMapID());

                if (playerList.size() > 1) {
                    for (GameCharacter target : playerList) {
                        if (target.getLevel() < min_level || target.getLevel() > max_level) {
                            status = 2;
                            break;
                        }
                    }

                    if (status == 0) {
                        GameMap map = createMapInstance(board.getMapID());

                        if (map != null) {
                            String participants = "";

                            for (GameCharacter player : playerList) {
                                participants += player.getUserID() + ":" + player.getID() + ",";
                            }

                            int board_id = db.createBoardGame(board.getType(), participants);
                            board = new GameBoard(board_id, map.getID(), board, mode, topic, min_level, max_level, playerList);

                            modeList.put(board_id, board);

                            float start_x = -2.5f * board.getWidth() / 2 + board.getStart()[1] * 2.5f;
                            float start_y = -5 + 2.5f * board.getHeight() / 2 - board.getStart()[0] * 2.5f;

                            for (GameCharacter player : playerList) {
                                player.setGameMode(board_id);
                                warp(player.getID(), map.getID(), start_x, start_y, 0.3f, 0);
                            }

                            GameBoardTile[][] pathTable = board.getPathTable();
                            String boardTable = "";

                            for (int i = 0; i < board.getHeight(); i++) {
                                for (int j = 0; j < board.getWidth(); j++) {
                                    if (pathTable[i][j] != null) {
                                        boardTable += i + "," + j + ":" + board.getTile(i, j).getType() + ";";
                                    }
                                }
                            }

                            ResponseBoard response = new ResponseBoard();
                            response.set(this);
                            response.setMode(mode);
                            response.setBoard(boardTable);
                            response.setWidth(board.getWidth());
                            response.setHeight(board.getHeight());
                            response.setStart(board.getStart());
                            response.setEnd(board.getEnd());

                            updateUsersQueue(response, getActivePartyUserList(character.getPartyID(), character.getMapID()));
                        }
                    }
                } else {
                    status = 1;
                }
            }
        }

        return status;
    }

    public int startSurvivalGame(int char_id, short type, short mode, short min_level, short max_level) {
        int status = 0;

        GameSurvivalMode gameMode = gameSurvivalModeParser.getSurvivalMode(type);

        if (gameMode != null) {
            GameCharacter character = getActiveCharacter(char_id);

            if (character != null) {
                if (getActiveUser(Constants.BUG_USER_ID) != null) {
                    Vector<GameCharacter> playerList = new Vector<GameCharacter>();

                    if (mode == 0) {
                        playerList.add(character);
                    } else if (mode == 1) {
                        playerList = partyList.get(character.getPartyID()).getPartyList(character.getMapID());
                    }

                    if (mode == 0 || mode == 1 && playerList.size() > 1) {
                        for (GameCharacter target : playerList) {
                            if (target.getLevel() < min_level || target.getLevel() > max_level) {
                                status = 3;
                                break;
                            }
                        }

                        if (mode == 1) {
                            status = 4;
                        }

                        if (status == 0) {
                            GameMap map = createMapInstance(gameMode.getMapID());

                            if (map != null) {
                                String participants = "";

                                for (GameCharacter player : playerList) {
                                    participants += player.getUserID() + ":" + player.getID() + ",";
                                }

                                int mode_id = getUniqueID();
                                gameMode = new GameSurvivalMode(mode_id, map.getID(), gameMode, mode, min_level, max_level, playerList);

                                modeList.put(mode_id, gameMode);

                                for (GameCharacter player : playerList) {
                                    player.setGameMode(mode_id);
                                    warp(player.getID(), map.getID(), gameMode.getStartPos(), gameMode.getStartH());
                                }

                                ResponseSurvivalMode response = new ResponseSurvivalMode();
                                response.set(this);
                                response.setMode(mode);
                                response.setMaxWave((short) gameMode.getMaxWave());

                                if (mode == 0) {
                                    updateUserQueue(response, getActiveUser(character.getUserID()));
                                } else if (mode == 1) {
                                    updateUsersQueue(response, getActivePartyUserList(character.getPartyID(), character.getMapID()));
                                }
                            }
                        }
                    } else {
                        status = 2;
                    }
                } else {
                    status = 1;
                }
            }
        }

        return status;

     */

    /**
     *
     * @param char_id
     * @return comma-separated string of character names and online statuses.
     */
    public String getBuddies(int char_id) {
        String buddies = "";
        List<GameCharacter> characters = db.getBuddyList(char_id);
        GameUser u = new GameUser(-1);

        for(GameCharacter c : characters) {
            getGameDB().getUser(c.getUserID(), u);
            c.setOnline(u.getOnline());
            buddies += c.getName() + " " + c.getOnline() + ",";
        }
        if(buddies.length() == 0)
            return "";
        return buddies.substring(0,buddies.length()-1); //remove trailing comma
    }
    /*
    public void setExperience(int char_id, int amount) {
        GameCharacter character = getActiveCharacter(char_id);

        if (character != null) {
            if (character.getLevel() < Constants.MAX_LEVEL) {
                int experience = character.getExperience() + amount;

                if (character.getLevel() < Constants.MAX_LEVEL - 1) {
                    int nextNextLevelExp = gameExpParser.getExperienceTotal(character.getLevel() + 2);

                    if (experience >= nextNextLevelExp) {
                        experience = Math.round(nextNextLevelExp * 0.9f);
                    }
                } else if (character.getLevel() == Constants.MAX_LEVEL - 1) {
                    experience = gameExpParser.getExperienceTotal(character.getLevel());
                }

                character.setExperience(experience);

                short lastLevel = character.getLevel();

                if (experience >= gameExpParser.getExperienceTotal(character.getLevel() + 1)) {
                    character.setLevel((short) (character.getLevel() + 1));
                }

                int exp_min = character.getExperience() - gameExpParser.getExperienceTotal(character.getLevel());
                int exp_max = gameExpParser.getExperienceNeeded(character.getLevel());

                ResponseExperience response = new ResponseExperience();
                response.set(this);
                response.setAmount(amount);
                response.setExperience(exp_min, exp_max);

                updateUserQueue(response, getActiveUser(character.getUserID()));

                if (character.getLevel() != lastLevel) {
                    int atk_power = Math.round(character.getLevel() * 0.75f);

                    character.setBaseAttackPower(atk_power, atk_power);
                    character.setBaseDefense((short) Math.round(character.getLevel() * 0.45f));
                    character.setBaseHealth(100 + (character.getLevel() - 1) * 5 + (int) Math.pow(character.getLevel() / 5, 2));

                    for (GameItem equipment : character.getEquipmentList().values()) {
                        GameItemEffectTable.applyEffects(1, equipment, character);
                    }

                    character.setHealth(character.getHealthMax());

                    ResponseLevel levelResponse = new ResponseLevel();
                    levelResponse.set(this);
                    levelResponse.setLevel(character.getLevel());
                    levelResponse.setTargetID(character.getID());

                    updateCharacterUsersQueue(levelResponse, getActiveUserList(character.getMapID()));

                    ResponseUpdateHealth healthResponse = new ResponseUpdateHealth();
                    healthResponse.set(this);
                    healthResponse.setHealth(character.getHealth(), character.getHealthMax());
                    healthResponse.setTargetID(char_id);

                    updateCharacterUsersQueue(healthResponse, getActiveUserList(character.getMapID()));

                    if (character.getPartyID() != 0) {
                        if (partyList.get(character.getPartyID()).getPartyList().size() > 1) {
                            ResponsePartyUpdate updateResponse = new ResponsePartyUpdate();
                            updateResponse.set(this);
                            updateResponse.setCharacter(character);

                            updateOthersQueue(updateResponse, getActivePartyUserList(character.getPartyID()), character.getUserID());
                        }
                    }

                    ResponseUpdateAttackPower attackResponse = new ResponseUpdateAttackPower();
                    attackResponse.set(this);
                    attackResponse.setAttackPower(character.getAttackMin(), character.getAttackMax());
                    attackResponse.setTargetID(char_id);

                    updateUserQueue(attackResponse, getActiveUser(character.getUserID()));

                    ResponseUpdateDefense defenseResponse = new ResponseUpdateDefense();
                    defenseResponse.set(this);
                    defenseResponse.setDefense(character.getDefense());
                    defenseResponse.setTargetID(char_id);

                    updateUserQueue(defenseResponse, getActiveUser(character.getUserID()));
                }
            }
        }
    }

    public void setExperienceDrop(int char_id, int amount) {
        setExperience(char_id, (int) (amount * Constants.EXPERIENCE_DROP_RATE));
    }

    public void setHealth(int target_id, int amount) {
        GameEntity target = getActiveEntity(target_id);

        if (target != null) {
            int health = target.getHealth();

            updateHealth(target_id, amount);

            ResponseHealth response = new ResponseHealth();
            response.set(this);
            response.setAmount(target.getHealth() - health);
            response.setTargetID(target_id);

            updateCharacterUsersQueue(response, getActiveUserList(target.getMapID()));
        }
    }

    public void setMoney(int char_id, int amount) {
        GameCharacter character = getActiveCharacter(char_id);

        if (character != null) {
            updateMoney(char_id, amount);

            ResponseMoney response = new ResponseMoney();
            response.set(this);
            response.setAmount(amount);

            updateUserQueue(response, getActiveUser(character.getUserID()));
        }
    }

    public void setMoneyDrop(int char_id, int amount) {
        setMoney(char_id, (int) (amount * Constants.MONEY_DROP_RATE));
    }

    public void updateHealth(int target_id, int amount) {
        GameEntity target = getActiveEntity(target_id);

        if (target != null) {
            target.setHealth(target.getHealth() + amount);

            ResponseUpdateHealth response = new ResponseUpdateHealth();
            response.set(this);
            response.setHealth(target.getHealth(), target.getHealthMax());
            response.setTargetID(target_id);

            updateCharacterUsersQueue(response, getActiveUserList(target.getMapID()));

            if (target.getUserID() != Constants.BUG_USER_ID) {
                GameCharacter character = (GameCharacter) target;

                if (character.getPartyID() != 0) {
                    if (partyList.get(character.getPartyID()).getPartyList().size() > 1) {
                        ResponsePartyUpdate updateResponse = new ResponsePartyUpdate();
                        updateResponse.set(this);
                        updateResponse.setCharacter(character);

                        updateOthersQueue(updateResponse, getActivePartyUserList(character.getPartyID()), character.getUserID());
                    }
                }
            }

            if (target.getHealth() == 0) {
                target.setStatus(true);

                ResponseDead deadResponse = new ResponseDead();
                deadResponse.set(this);
                deadResponse.setTargetID(target.getID());

                updateUsersQueue(deadResponse, getActiveUserList(target.getMapID()));

                if (target.getUserID() != Constants.BUG_USER_ID) {
                    GameCharacter character = (GameCharacter) target;

                    character.clearBattleList();
                    character.clearTargetList();
                }
            }
        }
    }

    public void updateMoney(int char_id, int amount) {
        GameCharacter character = getActiveCharacter(char_id);

        if (character != null) {
            character.setMoney(character.getMoney() + amount);
            updateAttribute(char_id, Constants.ATTRIBUTE_MONEY + ":" + character.getMoney() + ";");
        }
    }

    public void updateAttribute(int char_id, String attributes) {
        GameCharacter character = getActiveCharacter(char_id);

        if (character != null) {
            ResponseAttributeUpdate response = new ResponseAttributeUpdate();
            response.set(this);
            response.setAttributeList(attributes);

            updateUserQueue(response, getActiveUser(character.getUserID()));
        }
    }*/

    /**
     * Prints log message.  This function can
     * be changed to direct messages to a logfile.
     * @param msg the log message.
     */
    public void log(String msg, String type) {
		if(type.equals("so")) // std out
			System.out.println(msg);
		else if(type.equals("se")) // std err
			System.err.println(msg);
		else {
			FileWriter fw = null;
			try {
				if(type.equals("d")) // debug logfile
					fw = new FileWriter(Constants.LOG_DEBUG, true);
				else if(type.equals("e")) // error logfile
					fw = new FileWriter(Constants.LOG_ERROR, true);
				fw.write(msg+Constants.NEWLINE);
				fw.close();
			}catch(IOException e) {
				System.err.println("Error writing to log file:"  + msg);
			}
		}
    }
	public void log(String msg) {
		log(msg, "so");
	}

    /**
     * Server need to update other users' response queue when needed.
     * When one user changes the status, server need to inform others
     * to update the user's status in each client world.
     *
     * @param response		the response needs to be added to other user's queue
     * @param userId		the requested user's id
     * @param userList			the list of user need to be updated
     */
    public void updateOthersQueue(GameResponse response, Vector<GameUser> userList, int user_id) {
        if (GameServer.debugMode) {
            System.out.println("\t" + response.getClass());
            System.out.println("\t\tIn updateOthersQueue(): ");
        }

        for (GameUser user : userList) {
            if (GameServer.debugMode) {
                System.out.print("\t\t\t" + user.getUsername() + ".");
            }

            // Add the response to all the other user's queue
            // except for this requester
            if (user.getID() != user_id) {
                synchronized (user) {
                    user.queueUpdateResponse(response);

                    if (GameServer.debugMode) {
                        System.out.println("queueUpdateResponse()");
                    }
                }
            }

            if (GameServer.debugMode) {
                System.out.println();
            }
        }
    }

    public void addBuddyInvite(String username, Message m)
    {
        Vector<Message> messages = buddyInvites.get(username);
        if(messages == null) {
            messages = new Vector<Message>();
        }
        
        messages.add(m);
        buddyInvites.put(username, messages);
    }

    public Vector<Message> getBuddyInvites(String username)    {
        return buddyInvites.get(username);
    }
    public void clearBuddyInvites(String username)    {
        buddyInvites.remove(username);
    }

    public void addPrivateChat(String username, Message m)
    {
        Vector<Message> messages = privateChats.get(username);
        if(messages == null) {
            messages = new Vector<Message>();
        }

        messages.add(m);
        privateChats.put(username, messages);
    }

    public Vector<Message> getPrivateChats(String username)    {
        return privateChats.get(username);
    }
    public void clearPrivateChats(String username)    {
        privateChats.remove(username);
    }
    public void updateUserQueue(GameResponse response, GameUser user) {
        if (user != null) {
            synchronized (user) {
                user.queueUpdateResponse(response);
            }
        }
    }
    /*man-
    public void updateUsersQueue(GameResponse response, Vector<GameUser> userList) {
        for (GameUser user : userList) {
            synchronized (user) {
                user.queueUpdateResponse(response);
            }
        }
    }

    public void updateCharacterUsersQueue(GameResponse response, Vector<GameUser> userList) {
        for (GameUser user : userList) {
            if (user.getID() != Constants.BUG_USER_ID) {
                synchronized (user) {
                    user.queueUpdateResponse(response);
                }
            }
        }
    }

    public void updateCharacterUsersQueue(GameResponse response, Vector<GameUser> userList, int user_id) {
        for (GameUser user : userList) {
            if (user.getID() != user_id && user.getID() != Constants.BUG_USER_ID) {
                synchronized (user) {
                    user.queueUpdateResponse(response);
                }
            }
        }
    }

    public void restartBugServerMonitor() {
        if (bugServerMonitor != null) {
            if (!bugServerMonitor.isAlive()) {
                if (BugServerMonitor.OVERRIDE_MODE) {
                    BugServerMonitor.OVERRIDE_MODE = false;

                    System.out.println("Restarting Bug Server Monitor...");

                    bugServerMonitor = new BugServerMonitor(this, gameConfigReader.getBugServerPath());
                    bugServerMonitor.start();
                }
            }
        }
    }*/

    /**
     * @param args
     */
    public static void main(String[] args) {


        System.out.println("\n-----------------------\n");
        System.out.println("deBugger MMO v" +"\n");



        System.out.println("Starting Game Server...\n");

        GameServer myServer = new GameServer();
        /*man-
        // Set-up shutdown thread, and initiate it,
        GameServerShutdown shutdownThread = new GameServerShutdown(myServer);
        shutdownThread.start();*/

        // Run Game Server
        myServer.runServer();
    }
}
